#include "Semantico.h"

Semantico::Semantico()
{
}

Semantico::~Semantico()
{
}

bool Semantico::analisar(Arvore *pArvore, Hash *pHash, NoArvore *pNoErros)
{
    this->mTabelaHash = pHash;
    this->mArvore     = pArvore;
    this->mNoErro     = pNoErros; 

    // verifica se alguma funcao foi declarada duas vezes no mesmo escopo
    if (this->validarDeclaracaoFuncao(this->mArvore->getRaiz())   == false) return false;

    // verifica se alguma variavel foi declarada duas vezes no mesmo escopo
    if (this->validarDeclaracaoVariavel(this->mArvore->getRaiz()) == false) return false;

    // verifica se alguma variavel que esta sendo usada nao foi declarada
    if (this->validarVariaveisUsadas(this->mArvore->getRaiz())    == false) return false;

    return true;
}

bool Semantico::isNumero(std::string pIdentificador)
{
    for (int i = 0; i < pIdentificador.length(); i++)
        if (pIdentificador[i] < '0' || pIdentificador[i] > '9')
            return false;
    return true;
}

bool Semantico::isSimbolo(std::string pIdentificador)
{
    if (pIdentificador == "("  || pIdentificador == ")"   ||
        pIdentificador == "<"  || pIdentificador == ">"   ||
        pIdentificador == "<=" || pIdentificador == ">="  ||
        pIdentificador == "+"  || pIdentificador == "-"   ||
        pIdentificador == "*"  || pIdentificador == "div" ||
        pIdentificador == "="  || pIdentificador == ":="  ||
        pIdentificador == "or" || pIdentificador == "and" ||
        pIdentificador == "<>" || pIdentificador == ","   ||
        pIdentificador == "not"                             )
        return true;

    return false;
}

bool Semantico::isBooleano(std::string pIdentificador)
{
    if (pIdentificador == "true" || pIdentificador == "false")
        return true;

    return false;
}

std::string Semantico::buscarProcedimento(std::string pIdentificador, NoArvore *pNoPai)
{
    NoArvore *percorre = pNoPai->getNoPai();
    std::list<NoArvore *>::iterator it;

    while(percorre != NULL)
    {
        if (percorre->getConteudo() != NULL)
        {
            if (strcmp(percorre->getConteudo(), "[FUNCAO]")       == 0 ||
                strcmp(percorre->getConteudo(), "[PROCEDIMENTO]") == 0  )
            {
                if (percorre->temFilho() == true)
                {
                    it = percorre->getFilhos()->begin();

                    if (this->mTabelaHash->getHash(pIdentificador, (*it)->getConteudo()) != NULL)
                        return (*it)->getConteudo();
                }
            }
            else if (strcmp(percorre->getConteudo(), "program") == 0)
            {
                if (this->mTabelaHash->getHash(pIdentificador, "program") != NULL)
                    return "program";
            }
        }
        percorre = percorre->getNoPai();
    }
    return "";
}

bool Semantico::validarExpressao(std::list<std::string> *pExpressao, NoArvore *pNoPai, std::string &pRetorno)
{
    std::list<std::string>::iterator it;
    std::vector<TokenExpressao>::iterator itToken;
    std::vector<TokenExpressao> tokens;

    if (pExpressao == NULL) return true;

    for (it = pExpressao->begin(); it != pExpressao->end(); it++)
    {
        TokenExpressao t = {*it, "", "", 0};

        if (isSimbolo(*it) == true)
        {
            t.categoria = "simbolo";
        }
        else if (isNumero(*it) == true)
        {
            t.categoria = "numero";
            t.tipo      = "integer";
        }
        else if (isBooleano(*it) == true)
        {
            t.categoria = "booleano";
            t.tipo      = "boolean";
        }
        else
        {
            std::string procedimentoPai = this->buscarProcedimento(*it, pNoPai);

            int id = (this->mTabelaHash->getHash(*it, procedimentoPai))->getCategoria();
            
            if (id == 0)
            {
                IdentificadorVariavel *idVar = (IdentificadorVariavel *) this->mTabelaHash->getHash(*it, procedimentoPai);
                t.categoria = "variavel";
                t.tipo      = idVar->getTipo();
            }
            else if (id == 1)
            {
                IdentificadorParametro *idPar = (IdentificadorParametro *) this->mTabelaHash->getHash(*it, procedimentoPai);
                t.categoria = "variavel";
                t.tipo      = idPar->getTipo();
            }
            else if (id == 2)
            {
                if (tokens.size() > 0)
                {
                    this->mNoErro->inserirFilho("Procedimento nao tem retorno.");
                    return false;
                }
                IdentificadorProcFunc *idProc = (IdentificadorProcFunc *) this->mTabelaHash->getHash(*it, procedimentoPai);

                t.categoria  = "procedimento";
                t.tipo       = "";
                t.n          = idProc->getN();
                t.parametros = idProc->getParametros();
            }
            else if (id == 3)
            {
                IdentificadorProcFunc *idProc = (IdentificadorProcFunc *) this->mTabelaHash->getHash(*it, procedimentoPai);

                t.categoria  = "funcao";
                t.tipo       = idProc->getRetorno();
                t.n          = idProc->getN();
                t.parametros = idProc->getParametros();
            }
        }
        tokens.push_back(t);
    }

    if (this->fazerOperacoes(tokens) == false) return false;
    if (this->callFuncao(tokens)     == false) return false;
/*
    std::cout << "Chamadas realizadas: " << std::endl;
    for (int i = 0; i < tokens.size(); i++)
        std::cout << tokens[i].token << " ";
    std::cout << std::endl;
*/

    this->resolverComparacoes(tokens);    
    this->resolverOrAnd(tokens);

    pRetorno = tokens[0].tipo;

    return true;
}

bool Semantico::callFuncao(std::vector<TokenExpressao> &tokens)
{
    int index = 0, size = tokens.size();
    bool trocou;
    int maior;

    do
    {
        trocou = false;
        index  = 0;
        size   = tokens.size();
        maior  = -1;

        // procura pela ultima funcao da expressao
        while (size - index >= 2)
        {
            if ((tokens[index].categoria == "funcao" ||tokens[index].categoria == "procedimento") &&
                 tokens[index + 1].token == "(")
            {
                maior = index;
            }
            index++;
        }

        if (maior != -1)
        {
            trocou = true;

            int index = maior + 2;
            int indexParametro = 0;

            while (index < size)
            {
                if (indexParametro < tokens[maior].n)
                {
                    if (tokens[maior].parametros[indexParametro]->isPassagemReferencia() == true)
                    {
                        if (tokens[index].token == ")")
                        {
                            std::string erro = "Faltando parametros: " + tokens[maior].token;
                            this->mNoErro->inserirFilho(erro.c_str());
                            return false;
                        }
                        else if (tokens[index].categoria != "variavel")
                        {
                            std::string erro = "Erro no tipo da passagem: " + tokens[maior].token;
                            this->mNoErro->inserirFilho(erro.c_str());
                            return false;
                        }
                        else if (tokens[index].tipo != tokens[maior].parametros[indexParametro]->getTipo())
                        {
                            std::string erro = "Erro no tipo de parametro: " + tokens[maior].token;
                            this->mNoErro->inserirFilho(erro.c_str());
                            return false;
                        }
                    }
                    else
                    {
                        if (tokens[index].token == ")")
                        {
                            std::string erro = "Faltando parametros: " + tokens[maior].token;
                            this->mNoErro->inserirFilho(erro.c_str());
                            return false;
                        }
                        else if (tokens[index].categoria != "variavel" && tokens[index].categoria != "numero" && tokens[index].categoria != "funcao")
                        {                            
                            std::string erro = tokens[index].token + "Erro na passagem de parametros: " + tokens[maior].token;
                            this->mNoErro->inserirFilho(erro.c_str());
                            return false;
                        }

                        if (tokens[index].tipo != tokens[maior].parametros[indexParametro]->getTipo())
                        {
                            std::string erro = "Erro no tipo de parametro: " + tokens[maior].token;
                            this->mNoErro->inserirFilho(erro.c_str());
                            return false;
                        }
                    }
                    indexParametro++;
                }
                else
                {
                    if (tokens[index].token != ")")
                    {
                        std::string erro = "Erro no numero de parametros: " + tokens[maior].token;
                        this->mNoErro->inserirFilho(erro.c_str());
                        return false;
                    }
                    else
                    {
                        TokenExpressao t;

                        if (tokens[maior].tipo == "integer")
                        {
                            TokenExpressao aux = {"1", "numero", "integer", 0};
                            t = aux;
                        }
                        else if (tokens[maior].tipo == "boolean")
                        {
                            TokenExpressao aux = {"true", "booleano", "boolean", 0};
                            t = aux;
                        }
                        else
                        {
                            TokenExpressao aux = {"procedimento", "procedimento", "", 0};
                            t = aux;
                        }
                        tokens[maior] = t;
                        
                        for (int i = maior + 1; i < size - (indexParametro + 2 + indexParametro - 1); i++)
                            tokens[i] = tokens[i + (indexParametro + 2 + indexParametro - 1)];

                        for (int i = 0; i < indexParametro + 2 + indexParametro - 1; i++)
                            tokens.pop_back();

                        break;
                    }
                    
                }

                if (indexParametro == tokens[maior].n)
                {
                    index++;
                }
                else
                {
                    index += 2;
                    if (index >= size)
                    {
                        std::string erro = "Faltando parametros: " + tokens[maior].token;
                        this->mNoErro->inserirFilho(erro.c_str());
                        return false;
                    }
                }
            }
        }

        if (this->fazerOperacoes(tokens) == false) return false;

    } while (trocou == true);
    
    if (this->fazerOperacoes(tokens) == false) return false;

   return true;
}


bool Semantico::fazerOperacoes(std::vector<TokenExpressao> &tokens)
{
    int index, trocou;

    this->removerParenteses(tokens);

    do
    {
        index  = 0;
        trocou = 0;

        if (tokens.size() >= 2)
        {
            // -20 ... -soma(a+b)
            if ((tokens[index].token == "-" || tokens[index].token == "+") && tokens[index + 1].tipo == "integer")
            {
                tokens[index] = tokens[index + 1];
                for (int i = index + 1; i < tokens.size() - 1; i++)
                    tokens[i] = tokens[i + 1];
                tokens.pop_back();
                trocou = 1;
            }
        }

        while (tokens.size() - index >= 3)
        {
/*            if ((tokens[index].categoria     == "variavel" || tokens[index].categoria     == "funcao" || tokens[index].categoria   == "numero" ) &&
                (tokens[index + 2].categoria == "variavel" || tokens[index + 2].categoria == "funcao" || tokens[index+2].categoria == "numero" )   )*/
                
            if (tokens[index].tipo == "integer" && tokens[index + 2].tipo == "integer")
            {
                if (tokens[index + 2].n == 0)
                {
                    if (tokens[index + 1].token == "+"   || tokens[index + 1].token == "-" ||
                        tokens[index + 1].token == "div" || tokens[index + 1].token == "*"   )
                    {
                        TokenExpressao t = {"1", "numero", "integer", 0};
                        tokens[index]    = t;

                        for (int i = index + 1; i < tokens.size() - 2; i++)
                            tokens[i] = tokens[i + 2];

                        tokens.pop_back();
                        tokens.pop_back();
                        trocou = 1;
                    }
                }
            }
            // (-2  .... ,-a
            else if ((tokens[index].categoria == "simbolo") &&
                     (tokens[index + 1].token == "+" || tokens[index + 1].token == "-") &&
                      tokens[index + 2].tipo == "integer")
            {
                tokens[index + 1] = tokens[index + 2];
                for (int i = index + 2; i < tokens.size() - 1; i++)
                    tokens[i] = tokens[i + 1];
                tokens.pop_back();
                trocou = 1;
            }

            index++;
       }

       if (trocou == true) this->removerParenteses(tokens);

   } while (trocou == true);
   
   return true;
}

void Semantico::resolverOrAnd(std::vector<TokenExpressao> &tokens)
{
    int index, trocou;

    this->removerParenteses(tokens);

    do
    {
        index  = trocou = 0;

        while (tokens.size() - index >= 3)
        {
            if (tokens[index].tipo == "boolean" && tokens[index + 2].tipo == "boolean")
            {
                if (tokens[index + 1].token == "or" || tokens[index + 1].token == "and")
                {
                    TokenExpressao t = {"true", "booleano", "boolean", 0};

                    tokens[index] = t;

                    for (int i = index + 1; i < tokens.size() - 2; i++)
                        tokens[i] = tokens[i + 2];
                    tokens[index] = t;
                    tokens.pop_back();
                    tokens.pop_back();
                    trocou = 1;
                }
            }
            index++;
       }

       if (trocou == true) this->removerParenteses(tokens);

   } while (trocou == true);
}

void Semantico::resolverComparacoes(std::vector<TokenExpressao> &tokens)
{
    int index, trocou;

    this->removerParenteses(tokens);

    do
    {
        index = trocou = 0;

        while (tokens.size() - index >= 3)
        {
            if ((tokens[index].categoria     == "variavel" || tokens[index].categoria     == "funcao" || tokens[index].categoria   == "numero" || tokens[index].categoria     == "booleano" ) &&
                (tokens[index + 2].categoria == "variavel" || tokens[index + 2].categoria == "funcao" || tokens[index+2].categoria == "numero" || tokens[index + 2].categoria == "booleano" )   )
            {
                if (tokens[index + 2].n == 0)
                {
                    if (tokens[index + 1].token == "<"  || tokens[index + 1].token == ">"  ||
                        tokens[index + 1].token == "<=" || tokens[index + 1].token == ">=" ||
                        tokens[index + 1].token == "="  || tokens[index + 1].token == "<>"   )
                    {
                        TokenExpressao t = {"true", "booleano", "boolean", 0};
                        
                        for (int i = index + 1; i < tokens.size() - 2; i++)
                            tokens[i] = tokens[i + 2];
                        tokens[index] = t;
                        tokens.pop_back();
                        tokens.pop_back();
                        trocou = 1;
                    }
                }
            }

            index++;
       }

       index = 0;
       while (tokens.size() - index >= 2)
       {
            if ((tokens[index].token == "not" && tokens[index + 1].tipo == "boolean"))
            {
                TokenExpressao t = {"true", "booleano", "boolean", 0};

                tokens[index] = t;

                for (int i = index + 1; i < tokens.size() - 1; i++)
                    tokens[i] = tokens[i + 1];

                tokens.pop_back();
                trocou = 1;
            }
            index++;            
       }

       if (trocou) this->removerParenteses(tokens);

   } while (trocou);
   
}

/*--------------------------------------------------------------------
  Remove os parenteses que envolvem apenas um identificador ou numero.
  Exceto se o parenteses pertencer a uma funcao.
  Exemplo: ((a)) => a
---------------------------------------------------------------------*/
void Semantico::removerParenteses(std::vector<TokenExpressao> &tokens)
{
    int index, trocou;

    // tenta remover parentes enquanto houver remocao
    do
    {
        index  = 0;
        trocou = 0;

        // verifica se existe ao menos 3 tokens
        while (tokens.size() - index >= 3)
        {
            // verifica se existe parenteses entre algo        
            if (tokens[index].token == "(" && tokens[index + 2].token == ")")
            {
                // verifica se esse algo eh diferente de simbolo                
                if (tokens[index + 1].categoria != "simbolo")
                {
                    // verifica se pode existir algum elemento antes do primeiro parentes
                    if (index - 1 >= 0)
                    {
                        // verifica se esse elemento eh o nome de uma funcao, cancelando a remocao
                        if (tokens[index - 1].categoria == "funcao")
                        {
                            index++;
                            continue;
                        }
                    }

                    // remove os parentes e desloca todos os elementos para a esquerda
                    // e seta a variavel troca indicando que houve uma troca
                    tokens[index] = tokens[index + 1];
                    for (int i = index + 1; i < tokens.size() - 2; i++)
                        tokens[i] = tokens[i + 2];
                    tokens.pop_back();
                    tokens.pop_back();
                    trocou = 1;                    
                }
            }
            index++;
       }

   } while (trocou == 1); // enquanto houver trocas ele tenta remover mais parenteses
}

bool Semantico::variavelDeclarada(NoArvore *pNoComando, std::string pIdentificador)
{
    std::list<NoArvore *>::iterator it;
    std::list<NoArvore *>::iterator itVarPar;
    std::list<NoArvore *>::iterator itProcFunc;
    std::list<NoArvore *>::iterator procFunc;

    if (this->isSimbolo(pIdentificador) || this->isNumero(pIdentificador) || this->isBooleano(pIdentificador))
        return true;

    bool     sair   = false;
    NoArvore *noPai = pNoComando->getNoPai();

    if (noPai->temFilho() == false) return true;

    while (sair == false)
    {
        for (it = noPai->getFilhos()->begin(); it != noPai->getFilhos()->end(); it++)
        {
            if ((*it)->getConteudo() != NULL)
            {
                // verifica o nome do procedimento ou funcao
                if (pIdentificador == (*it)->getConteudo())
                {
                    return true;
                }
                else if (strcmp((*it)->getConteudo(), "[VARIAVEIS]")  == 0 ||
                         strcmp((*it)->getConteudo(), "[PARAMETROS]") == 0  )
                {
                    if ((*it)->temFilho() == true)
                    {
                        for (itVarPar = (*it)->getFilhos()->begin(); itVarPar != (*it)->getFilhos()->end(); itVarPar++)
                        {
                            if ((*itVarPar)->getConteudo() != NULL)
                            {
                                if (pIdentificador == (*itVarPar)->getConteudo())
                                    return true;
                            }
                        }
                    }
                }
                else
                {
                    // verifica o nome do procedimento ou funcao
                    if (strcmp((*it)->getConteudo(), "[FUNCOES|PROCEDIMENTOS]") == 0)
                    {
                        if ((*it)->temFilho() == true)
                        {
                            for (itProcFunc = (*it)->getFilhos()->begin(); itProcFunc != (*it)->getFilhos()->end(); itProcFunc++)
                            {
                                if ((*itProcFunc)->getConteudo() != NULL)
                                {
                                    
                                    if (strcmp((*itProcFunc)->getConteudo(), "[FUNCAO]")       == 0 ||
                                        strcmp((*itProcFunc)->getConteudo(), "[PROCEDIMENTO]") == 0  )
                                    {
                                        procFunc = (*itProcFunc)->getFilhos()->begin();
                                        if (pIdentificador == (*procFunc)->getConteudo())
                                            return true;
                                    }
                                }
                            } // for (itProcFunc = (*it)->getFilhos()->begin(); itProcFunc != (*it)->getFilhos()->end(); itProcFunc++)
                        } // if ((*it)->temFilho() == true)
                    } // if (strcmp((*it)->getConteudo(), "[FUNCOES|PROCEDIMENTOS]") == 0)
                } // else
            } // if ((*it)->getConteudo() != NULL)
        } // for (it = noPai->getFilhos()->begin(); it != noPai->getFilhos()->end(); it++)

        // caso o noh atual nao seja o program ele vai subindo a arvore ate chegar nele
        if (strcmp(noPai->getConteudo(), "program") == 0)
        {
            sair = true;
        }
        else
        {
            noPai = noPai->getNoPai();
            noPai = noPai->getNoPai();
        }
    }

    std::string erro = "Variavel nao declarada: " + pIdentificador;
    this->mNoErro->inserirFilho(erro.c_str());
    return false;
}

bool Semantico::percorrerComandos(NoArvore *pNoComando)
{
    std::list<NoArvore *>::iterator  itComando;
    std::list<NoArvore *>::iterator  it;
    std::list<NoArvore *>::iterator  itAux;
    std::list<std::string>::iterator expressao;

    if (pNoComando->temFilho() == false) return true;

    for (itComando = pNoComando->getFilhos()->begin(); itComando != pNoComando->getFilhos()->end(); itComando++)
    {
        if ((*itComando)->getConteudo() != NULL)
        {
            if (strcmp((*itComando)->getConteudo(), "write")   == 0 ||
                strcmp((*itComando)->getConteudo(), "writeln") == 0  )
            {
                if ((*itComando)->temFilho() == true)
                for (it = (*itComando)->getFilhos()->begin(); it != (*itComando)->getFilhos()->end(); it++)
                {
                    for (expressao = (*it)->getExpressao()->begin(); expressao != (*it)->getExpressao()->end(); expressao++)
                    {
                        if (this->variavelDeclarada(pNoComando, (*expressao)) == false) return false;
                    }

                    std::string retorno;
                    if (this->validarExpressao((*it)->getExpressao(), pNoComando, retorno) == false) return false;

                    if (retorno != "integer")
                    {
                        std::string erro = "Retorno deve ser do tipo integer: ";
                        for (expressao = (*it)->getExpressao()->begin(); expressao != (*it)->getExpressao()->end(); expressao++)
                            erro = erro + *expressao + " ";

                        this->mNoErro->inserirFilho(erro.c_str());
                        return false;
                    }

                }
            }
            else if (strcmp((*itComando)->getConteudo(), "read") == 0)
            {
                for (it = (*itComando)->getFilhos()->begin(); it != (*itComando)->getFilhos()->end(); it++)
                {
                    if (this->variavelDeclarada(pNoComando, (*it)->getConteudo()) == false) return false;
                }
            }
            else if (strcmp((*itComando)->getConteudo(), ":=") == 0)
            {
                std::string tipo = "integer";

                for (it = (*itComando)->getFilhos()->begin(); it != (*itComando)->getFilhos()->end(); it++)
                {
                    if ((*it)->getConteudo() != NULL)
                    {
                        if (this->variavelDeclarada(pNoComando, (*it)->getConteudo()) == false) return false;
                        
                        std::string procedimentoPai = this->buscarProcedimento((*it)->getConteudo(), pNoComando);

                        int id = (this->mTabelaHash->getHash((*it)->getConteudo(), procedimentoPai))->getCategoria();
            
                        if (id == 0)
                        {
                            IdentificadorVariavel *idVar = (IdentificadorVariavel *) this->mTabelaHash->getHash((*it)->getConteudo(), procedimentoPai);
                            tipo      = idVar->getTipo();
                        }
                        else if (id == 1)
                        {
                            IdentificadorParametro *idPar = (IdentificadorParametro *) this->mTabelaHash->getHash((*it)->getConteudo(), procedimentoPai);
                            tipo      = idPar->getTipo();
                        }
                        else if (id == 2)
                        {
                            this->mNoErro->inserirFilho("Atribuicao nao permitida.");
                            return false;
                        }
                        else if (id == 3)
                        {
                            IdentificadorProcFunc *idProc = (IdentificadorProcFunc *) this->mTabelaHash->getHash((*it)->getConteudo(), procedimentoPai);
                            tipo = idProc->getRetorno();
                        }

                    }
                    else
                    {
                        for (expressao = (*it)->getExpressao()->begin(); expressao != (*it)->getExpressao()->end(); expressao++)
                            if (this->variavelDeclarada(pNoComando, (*expressao)) == false) return false;

                        std::string retorno;
                        if (this->validarExpressao((*it)->getExpressao(), pNoComando, retorno) == false) return false;
                        
                        if (retorno != tipo)
                        {
                            std::string erro = "Retorno deve ser do tipo " + tipo + ": ";
                            for (expressao = (*it)->getExpressao()->begin(); expressao != (*it)->getExpressao()->end(); expressao++)
                                erro = erro + *expressao + " ";

                            this->mNoErro->inserirFilho(erro.c_str());

                            return false;
                        }
                    }
                }
            }
            else if (strcmp((*itComando)->getConteudo(), "if") == 0 || strcmp((*itComando)->getConteudo(), "while") == 0)
            {
                if ((*itComando)->temFilho() == true)
                for (it = (*itComando)->getFilhos()->begin(); it != (*itComando)->getFilhos()->end(); it++)
                {
                    if ((*it)->getConteudo() != NULL)
                    {
                        if (strcmp((*it)->getConteudo(), "[condiction]") == 0)
                        {
                            itAux = (*it)->getFilhos()->begin();
                            for (expressao = (*itAux)->getExpressao()->begin(); expressao != (*itAux)->getExpressao()->end(); expressao++)
                                if (this->variavelDeclarada(pNoComando, (*expressao)) == false) return false;

                            std::string retorno;
                            if (this->validarExpressao((*itAux)->getExpressao(), pNoComando, retorno) == false) return false;

                            if (retorno != "boolean")
                            {
                                std::string erro = "Expressao deve ser do tipo booleana: ";
                                for (expressao = (*itAux)->getExpressao()->begin(); expressao != (*itAux)->getExpressao()->end(); expressao++)
                                    erro = erro + *expressao + " ";
                                
                                this->mNoErro->inserirFilho(erro.c_str());
                                return false;
                            }
                        }
                        else if (strcmp((*it)->getConteudo(), "[then]") == 0 ||
                                 strcmp((*it)->getConteudo(), "[else]") == 0 ||
                                 strcmp((*it)->getConteudo(), "[do]")   == 0  )
                        {
                            if (this->percorrerComandos(*it) == false) return false;
                        }
                    }
                }
            }
            else if (strcmp((*itComando)->getConteudo(), "[CALL PROCEDURE|CALL FUNCTION]") == 0)
            {
                it = (*itComando)->getFilhos()->begin();

                for (expressao = (*it)->getExpressao()->begin(); expressao != (*it)->getExpressao()->end(); expressao++)
                {
                    if (this->variavelDeclarada(pNoComando, (*expressao)) == false) return false;
                }

                std::string retorno = "";
                if (this->validarExpressao((*it)->getExpressao(), pNoComando, retorno) == false) return false;
                
                if (retorno == "integer" ||retorno == "boolean")
                {
                    std::string erro = "Chamada de funcao nao permitida: ";
                    for (expressao = (*it)->getExpressao()->begin(); expressao != (*it)->getExpressao()->end(); expressao++)
                        erro = erro + *expressao + " ";
                    this->mNoErro->inserirFilho(erro.c_str());
                    return false;
                }
            }
        } // if ((*itComando)->getConteudo() != NULL)
    } // for (itComando = pNoComando->getFilhos()->begin(); itComando != pNoComando->getFilhos()->end(); itComando++)
    
    return true;
}

bool Semantico::validarVariaveisUsadas(NoArvore *pNoAtual)
{
    std::list<NoArvore *>::iterator it;

    if (pNoAtual->temFilho() == false) return true;

    // percorre todos os filhos do no atual a busca do no [COMANDOS]
    for (it = pNoAtual->getFilhos()->begin(); it != pNoAtual->getFilhos()->end(); it++)
    {
        if ((*it)->getConteudo() != NULL)
        {
            // verifica se eh o no [COMANDOS]
            if (strcmp((*it)->getConteudo(), "[COMANDOS]") == 0)
            {
                if (this->percorrerComandos(*it) == false) return false;
            }
            // verifica se eh um no que pode ter o no [COMANDOS]
            else if (strcmp((*it)->getConteudo(), "[FUNCOES|PROCEDIMENTOS]") == 0 ||
                     strcmp((*it)->getConteudo(), "[FUNCAO]")                == 0 ||
                     strcmp((*it)->getConteudo(), "[PROCEDIMENTO]")          == 0   )
            {
                if (this->validarVariaveisUsadas(*it) == false) return false;
            }
        }
    }

    return true;
}

bool Semantico::percorrerFuncoes(NoArvore *pNoFuncProc)
{
    std::list<NoArvore *>::iterator  itProcFunc;
    std::list<NoArvore *>::iterator  itDeclaracao;

    std::list<char *>           funcProcDeclarados;
    std::list<char *>::iterator itLista;

    if (pNoFuncProc->temFilho() == false) return true;

    for (itProcFunc = pNoFuncProc->getFilhos()->begin(); itProcFunc != pNoFuncProc->getFilhos()->end(); itProcFunc++)
    {
        if ((*itProcFunc)->getConteudo() != NULL)
        {
            if (strcmp((*itProcFunc)->getConteudo(), "[FUNCAO]")       == 0 ||
                strcmp((*itProcFunc)->getConteudo(), "[PROCEDIMENTO]") == 0   )
            {
                // recebe o primeiro filho de [FUNCAO], que eh o seu nome
                itDeclaracao = (*itProcFunc)->getFilhos()->begin();

                if ((*itDeclaracao)->getConteudo() != NULL)
                {
                    for (itLista = funcProcDeclarados.begin(); itLista != funcProcDeclarados.end(); itLista++)
                    {
                        if (strcmp((*itLista), (*itDeclaracao)->getConteudo()) == 0)
                        {
                            std::string aux = (*itDeclaracao)->getConteudo();
                            std::string erro = "Duas funcoes/procedimentos com identificadores iguais no mesmo escopo: " + aux;
                            this->mNoErro->inserirFilho(erro.c_str());
                            return false;
                        }                        
                    }
                    funcProcDeclarados.push_back((*itDeclaracao)->getConteudo());
                }
            }
        }
    }
    return true;
}

bool Semantico::validarDeclaracaoFuncao(NoArvore *pNoAtual)
{
    std::list<NoArvore *>::iterator it;

    if (pNoAtual->temFilho() == false) return true;

    // percorre todos os filhos do no atual a busca do no [FUNCOES|PROCEDIMENTOS]
    for (it = pNoAtual->getFilhos()->begin(); it != pNoAtual->getFilhos()->end(); it++)
    {
        if ((*it)->getConteudo() != NULL)
        {
            // verifica se eh o no [FUNCOES|PROCEDIMENTOS]
            if (strcmp((*it)->getConteudo(), "[FUNCOES|PROCEDIMENTOS]") == 0)
            {
                if (this->validarDeclaracaoFuncao(*it) == false) return false;

                if (this->percorrerFuncoes(*it) == false)        return false;
            }
            // verifica se eh um no que pode ter o no [FUNCOES|PROCEDIMENTOS]
            else if (strcmp((*it)->getConteudo(), "[FUNCAO]")       == 0 ||
                     strcmp((*it)->getConteudo(), "[PROCEDIMENTO]") == 0   )
            {
                if (this->validarDeclaracaoFuncao(*it) == false) return false;
            }
        }
    }

    return true;
}


bool Semantico::percorrerVariaveis(NoArvore *pNoVariavel)
{
    std::list<NoArvore *>::iterator  itVariavel;
    std::list<NoArvore *>::iterator  it;
    std::list<NoArvore *>::iterator  itProcFunc;

    std::list<char *>           variaveisDeclaradas;
    std::list<char *>::iterator itLista;

    if (pNoVariavel->temFilho() == false) return true;

    // pega os identifadores do noh [parametro] para nao existir identificador com mesmo nome em [parametros] e [variaveis]
    if (strcmp(pNoVariavel->getConteudo(), "[VARIAVEIS]") == 0)
    {
        NoArvore *paiVariavel = pNoVariavel->getNoPai();
        for (itVariavel = paiVariavel->getFilhos()->begin(); itVariavel != paiVariavel->getFilhos()->end(); itVariavel++)
        {
            if ((*itVariavel)->getConteudo() != NULL)
            {
                if (strcmp((*itVariavel)->getConteudo(), "[PARAMETROS]") == 0)
                {
                    if ((*itVariavel)->temFilho() == true)
                    {
                        for (it = (*itVariavel)->getFilhos()->begin(); it != (*itVariavel)->getFilhos()->end(); it++)
                        {
                            variaveisDeclaradas.push_back((*it)->getConteudo());
                        }
                    }
                }
            }
        }
    }

    // pega os identifadores do noh [funcoes|procedimentos] para nao existir identificador com mesmo nome em [procedimentos] e [variaveis]
    if (strcmp(pNoVariavel->getConteudo(), "[VARIAVEIS]") == 0 || strcmp(pNoVariavel->getConteudo(), "[PARAMETROS]") == 0)
    {
        NoArvore *paiVariavel = pNoVariavel->getNoPai();
        for (itVariavel = paiVariavel->getFilhos()->begin(); itVariavel != paiVariavel->getFilhos()->end(); itVariavel++)
        {
            if ((*itVariavel)->getConteudo() != NULL)
            {
                if (strcmp((*itVariavel)->getConteudo(), "[FUNCOES|PROCEDIMENTOS]") == 0)
                {
                    if ((*itVariavel)->temFilho() == true)
                    {
                        for (it = (*itVariavel)->getFilhos()->begin(); it != (*itVariavel)->getFilhos()->end(); it++)
                        {
                            itProcFunc = (*it)->getFilhos()->begin();
                            variaveisDeclaradas.push_back((*itProcFunc)->getConteudo());
                        }
                    }
                }
            }
        }
    }

    for (itVariavel = pNoVariavel->getFilhos()->begin(); itVariavel != pNoVariavel->getFilhos()->end(); itVariavel++)
    {
        if ((*itVariavel)->getConteudo() != NULL)
        {
            for (itLista = variaveisDeclaradas.begin(); itLista != variaveisDeclaradas.end(); itLista++)
            {
                if (strcmp((*itLista), (*itVariavel)->getConteudo()) == 0)
                {
                    std::string aux = (*itLista);
                    std::string erro = "Duas variaveis com identificadores iguais no mesmo escopo: " + aux;
                    this->mNoErro->inserirFilho(erro.c_str());
                    return false;
                }                        
            }
            variaveisDeclaradas.push_back((*itVariavel)->getConteudo());
        }
    }
    return true;
}

bool Semantico::validarDeclaracaoVariavel(NoArvore *pNoAtual)
{
    std::list<NoArvore *>::iterator it;

    if (pNoAtual->temFilho() == false) return true;

    // percorre todos os filhos do no atual a busca do no [VARIAVEIS]
    for (it = pNoAtual->getFilhos()->begin(); it != pNoAtual->getFilhos()->end(); it++)
    {
        if ((*it)->getConteudo() != NULL)
        {
            // verifica se eh um noh [VARIAVEIS]
            if (strcmp((*it)->getConteudo(), "[VARIAVEIS]")  == 0 ||
                strcmp((*it)->getConteudo(), "[PARAMETROS]") == 0  )
            {
                if (this->percorrerVariaveis(*it) == false) return false;
            }
            // verifica se eh um noh que pode ter um noh [VARIAVEIS]
            else if (strcmp((*it)->getConteudo(), "[FUNCOES|PROCEDIMENTOS]") == 0 ||
                     strcmp((*it)->getConteudo(), "[FUNCAO]")                == 0 ||
                     strcmp((*it)->getConteudo(), "[PROCEDIMENTO]")          == 0   )
            {
                if (this->validarDeclaracaoVariavel(*it) == false) return false;
            }
        }
    }

    return true;
}
